//>>>exception
//>>>template

#include<iostream>
using namespace std;

class RuntimeException {
/*
private:
   string msg;
public:
   explicit RuntimeException(const string &m) {
      msg=m;
   }
*/
};


class LinkedList {
private:
   class ListNode {
   public:
      int value;
      ListNode *next;
      ListNode(int v, ListNode *n=NULL): value(v), next(n) { }
      void print() {
         cout<<"[ "<<value<<", "<<next<<"]"<<endl;
      }
   private:
      ListNode(const ListNode &that) { 
         *this=that;
      }
      const ListNode & operator=(const ListNode &that) {
         this->value=that.value;
         this->next=NULL;
         return *this;
      }
      friend void testCopyOfListNode(); 
   };
   friend void testCopyOfListNode(); 

public:
   LinkedList(): head(NULL) {} //!
   LinkedList(LinkedList &that): head(NULL) { //>>> initialization list
      *this=that;
   }
   const LinkedList & operator=(const LinkedList &that) { //>>> const?
      if(this==&that) {
         //cerr<<"copy to self"<<endl;
         return that; //same object
      }
      clearAll();
      //if(that.isEmpty()) return *this;
      for(ListNode *p=that.head; p!=NULL; p=p->next) {
         append(p->value); //>>> use copy-ctor?
      }
      return *this; //>>> which to return?
   }
   ~LinkedList() {
      clearAll();
   }

   void clearAll() {
      clearAllV1();
   }

   void prepend(int v) {
      if(isEmpty()) { 
         head=new ListNode(v);
      } else { 
         head=new ListNode(v, head);
      }
   }
   void append(int v) {
      ListNode *newNode=new ListNode(v);
      if(isEmpty()) {
         head=newNode;
         return;
      }
      ListNode *tail=findTail();
      tail->next=newNode;
   }
   int deleteHead() {
      if(isEmpty()) {
         //cerr<<"nothing to delete";
         throw RuntimeException();
      }
      int res=head->value;
      ListNode *temp=head;
      head=head->next;
      delete temp;
      return res;
   }
   int deleteTail() {
      //size==0
      if(isEmpty()) {
         //cerr<<"nothing to delete";
         throw RuntimeException();
      }
      
      //size==1
      if(head->next==NULL) {
         int res=head->value;
         delete head;
         head=NULL; //: important!
         return res;
      }

      //size==2 or more
      ListNode *nextToTail=head;
      ListNode *tail=nextToTail->next;
      while(tail->next!=NULL) { 
         nextToTail=tail;
         tail=tail->next; 
      } 
      int res=tail->value;
      nextToTail->next=NULL;
      delete tail;
      return res;
   }

   int length() const { return size(); }
   int size() const {
      int count=0;
      for(ListNode *p=head; p!=NULL; p=p->next) {
         count++;
      }
      return count;
   }
   bool isEmpty() const {
      if(head==NULL) return true;
      else return false;
   }
   void print() const {
      cout<<"[ ";
      for(ListNode *p=head; p!=NULL; p=p->next) {
         cout<<p->value<<" ";
      }
      cout<<"]"<<endl;
   }

private:
   ListNode *head;

   ListNode* findTail() const {
      if(isEmpty()) return NULL; //: is it ok?
      ListNode *tail=head;
      while(tail->next!=NULL) { tail=tail->next; } 
      return tail;
   }
   void clearAllV1() {
      if(isEmpty()) return;
      ListNode *temp;
      while(head!=NULL) {
         temp=head;
         head=head->next;
         delete temp;
      }
   }

   void clearAllV2() {
      while(!isEmpty()) {
         deleteHead();
      }
   }
};

//[ test==========================================
void testAppend() {
   LinkedList list;
   for(int i=0; i<10; i++) {
      list.append(i);
   }
   list.print();
}
void testPrepend() {
   LinkedList list;
   for(int i=0; i<10; i++) {
      list.prepend(i);
   }
   list.print();
}
void testDeleteHead() {
   LinkedList list;
   for(int i=0; i<10; i++) {
      list.append(i);
   }
   list.print();
   for(int i=0; i<10; i++) {
      cout<<list.deleteHead()<<" "<<endl;
      list.print();
   }
}
void testDeleteTail() {
   LinkedList list;
   for(int i=0; i<10; i++) {
      list.append(i);
   }
   list.print();
   for(int i=0; i<10; i++) {
      cout<<list.deleteTail()<<" "<<endl;
      list.print();
   }
}
void testClearAll() {
   LinkedList *list=new LinkedList; 
   for(int i=0; i<10; i++) {
      list->append(i);
   }
   list->print();
   list->clearAll();
   list->print();
   for(int i=0; i<10; i++) {
      list->append(i);
   }
   list->print();
}
void testDestructor() {
   LinkedList *list=new LinkedList; //>>> parentheses?
   for(int i=0; i<10; i++) {
      list->append(i);
   }
   list->print();
   delete list;
}
void testCopyCtor() {
   LinkedList a;
   for(int i=0; i<10; i++) {
      a.append(i);
   }
   a.print();
   LinkedList b=a;
   b.print();
   a.append(99);
   a.print();
   b.print();
}
void testAssignmentEquals() {
   LinkedList a;
   for(int i=0; i<10; i++) {
      a.append(i);
   }
   a.print();
   a=a;
   LinkedList b;
   b=a;
   b.print();
   a.append(99);
   a.print();
   b.append(100);
   b.print();
}
void testSize() {
   LinkedList a;
   cout<<a.size()<<endl;
   for(int i=0; i<10; i++) {
      a.append(i);
   }
   cout<<a.size()<<endl;
   a.clearAll();
   cout<<a.size()<<endl;
}
void testRuntimeException() {
   LinkedList a;
   for(int i=0; i<10; i++) {
      a.append(i);
   }
   a.print();
   for(int i=0; i<10; i++) {
      a.deleteHead();
   }
   a.deleteHead();
}
void testCopyOfListNode() {
   LinkedList::ListNode a(3);
   LinkedList::ListNode b(5);
   a.print();
   b.print();
   a=b;
   a.print();
   b.print();
   
   LinkedList::ListNode c(a);
   c.print();

}
int main() {
   //testAppend();
   //testPrepend();
   //testDeleteHead();
   //testDeleteTail();
   //testClearAll();
   //testDestructor();
   //testCopyCtor();
   //testAssignmentEquals();
   //testSize();
   //testRuntimeException();
   testCopyOfListNode();
   return 0;
}
